package top.jle.wx.proxy.feign.redPacket;


import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.*;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import top.jle.jpa.basic.util.ExceptionUtils;
import top.jle.wx.proxy.domain.redpack.GetTransferInfoDTO;
import top.jle.wx.proxy.domain.redpack.TransferDTO;

import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
public class TransferV3APIFeign {
    @Value("${ssl.key-store.root}")
    private String keyStoreRoot;
    private static final TimedCache<String, TransferBatchService> clientCache = CacheUtil.newTimedCache(Duration.ofSeconds(30L).toMillis());
    private final DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    @PostConstruct
    public void initCacheSchedule(){
        log.info("initCacheSchedule...");
        clientCache.schedulePrune(Duration.ofSeconds(30L).toMillis());
    }
    public TransferBatchService getClient(String mchId) throws Exception {
        if(StringUtils.isBlank(mchId)){
            ExceptionUtils.throwInvalidParamException();
        }
        TransferBatchService client;
        if(!clientCache.containsKey(mchId)){
            try(FileInputStream snStream = new FileInputStream(keyStoreRoot+"/"+mchId+"/V3/certificateSerialNumber.txt")){
                String certificatePath = keyStoreRoot+"/"+mchId+"/V3/certificate.p12";
                String privateKeyPath = keyStoreRoot+"/"+mchId+"/V3/privateKey.txt";
                String mchSerialNumber = IOUtils.toString(snStream, StandardCharsets.UTF_8).trim();
                log.info("开始构建商户号:{} API_V3配置 构建参数 certificatePath:{},privateKeyPath:{},mchSerialNumber:{}",mchId,certificatePath,privateKeyPath,mchSerialNumber);
                RSAConfig config = new RSAConfig.Builder()
                        .merchantId(mchId)
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(mchSerialNumber)
                        .wechatPayCertificatesFromPath(certificatePath)
                        .build();

                client = new TransferBatchService.Builder().config(config).build();
                log.info("商户号:{}证书构建完成",mchId);
                clientCache.put(mchId,client);
            }

        }else{
            //直接从缓存获取Client
            client = clientCache.get(mchId);
        }


        return client;
    }

    public JSONObject getTransferInfo(GetTransferInfoDTO dto) throws Exception {
        JSONObject result = new JSONObject();
        TransferBatchService service = getClient(dto.getMch_id());
        GetTransferDetailByOutNoRequest request = new GetTransferDetailByOutNoRequest();
        request.setOutBatchNo(dto.getPartner_trade_no());
        request.setOutBatchNo(dto.getPartner_trade_no());
        //为避免调用端重复开发逻辑 result需要保持V2的数据结构
        result.put("result_code","FAIL");
        try {
            TransferDetailEntity transferDetailEntity = service.getTransferDetailByOutNo(request);
            if("SUCCESS".equals(transferDetailEntity.getDetailStatus())){
                result.put("send_time",LocalDateTime.parse(transferDetailEntity.getUpdateTime(),DateTimeFormatter.ISO_OFFSET_DATE_TIME).format(ftf));
                result.put("result_code","SUCCESS");
            }else{
                result.put("err_code",transferDetailEntity.getFailReason().toString());
            }

        }catch (ServiceException e){
            log.warn(e.getMessage());
            result.put("err_code",e.getErrorCode());
        }
        return result;
    }

    public JSONObject sendTransfer(TransferDTO dto) throws Exception{
        JSONObject result = new JSONObject();
        //为避免调用端重复开发逻辑 result需要保持V2的数据结构
        result.put("result_code","FAIL");
        TransferBatchService service = getClient(dto.getMchid());

        InitiateBatchTransferRequest initiateBatchTransferRequest =
                new InitiateBatchTransferRequest();
        initiateBatchTransferRequest.setAppid(dto.getMch_appid());
        initiateBatchTransferRequest.setOutBatchNo(dto.getPartner_trade_no());
        initiateBatchTransferRequest.setBatchName(dto.getDesc());
        initiateBatchTransferRequest.setBatchRemark(dto.getDesc());
        initiateBatchTransferRequest.setTotalAmount(dto.getAmount().longValue());
        initiateBatchTransferRequest.setTotalNum(1);
        List<TransferDetailInput> transferDetailListList = new ArrayList<>();

        TransferDetailInput transferDetailInput = new TransferDetailInput();

        transferDetailInput.setOutDetailNo(dto.getPartner_trade_no());

        transferDetailInput.setTransferAmount(dto.getAmount().longValue());

        transferDetailInput.setTransferRemark(dto.getDesc());

        transferDetailInput.setOpenid(dto.getOpenid());

        transferDetailListList.add(transferDetailInput);
        initiateBatchTransferRequest.setTransferDetailList(
                transferDetailListList);
//        initiateBatchTransferRequest.setTransferSceneId("1000");
        try{
            InitiateBatchTransferResponse response = service.initiateBatchTransfer(initiateBatchTransferRequest);
            GetTransferBatchByNoRequest getTransferBatchByNoRequest = new GetTransferBatchByNoRequest();
            getTransferBatchByNoRequest.setBatchId(response.getBatchId());
            getTransferBatchByNoRequest.setNeedQueryDetail(true);
            AtomicInteger times = new AtomicInteger(0);
            Timer getResultTimer = new Timer("getResultTimer");
            final CountDownLatch latch = new CountDownLatch(1);
            getResultTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    boolean finish = false;
                    int time = times.getAndIncrement();
                    try {
                        //获取发送结果
                        TransferBatchEntity transferBatchEntity = service.getTransferBatchByNo(getTransferBatchByNoRequest);
                        List<TransferDetailCompact> detailList = transferBatchEntity.getTransferDetailList();
                        if(!CollectionUtils.isEmpty(detailList)){
                            TransferDetailCompact detail = detailList.get(0);
                            switch (detail.getDetailStatus()) {
                                //状态验证
                                case "SUCCESS" -> {
                                    //转账成功直接结束定时任务
                                    result.put("result_code", "SUCCESS");
                                    finish = true;
                                }
                                case "FAIL" -> {
                                    //转账失败 获取明细单状态
                                    GetTransferDetailByNoRequest request = new GetTransferDetailByNoRequest();
                                    request.setBatchId(response.getBatchId());
                                    request.setDetailId(detail.getDetailId());
                                    TransferDetailEntity transferDetailEntity = service.getTransferDetailByNo(request);
                                    result.put("result_code", "FAIL");
                                    result.put("err_code", transferDetailEntity.getFailReason().toString());
                                    finish = true;
                                }
                            }
                        }
                    } catch (Exception e) {
                       log.error(e.getMessage());
                    }
                    if (finish || time==5) {
                        //成功获取到回执 或者 5次都没有返回发送状态则不再执行
                        getResultTimer.cancel();
                        latch.countDown();
                    }
                }
            }, 50, 200); //首次获取状态发送接口完成后的50毫秒后执行，以后每200毫秒执行一次
            latch.await();
        }catch (ServiceException e){
            log.error(e.getMessage(),e);
            result.put("result_code","FAIL");
            switch (e.getHttpStatusCode()) {
                //下面两个错误码较V2有细微变化 为保证逻辑重用，这里做了转换
                case 429 -> result.put("err_code", "FREQ_LIMIT");
                case 500 -> result.put("err_code", "SYSTEMERROR");
                default -> result.put("err_code", e.getErrorCode());
            }

        }
        return result;

    }

}
